import { Injectable } from '@angular/core';
import { map, Observable, Subject, tap } from 'rxjs';

import { BaseHttpService } from '@core/base/base-http.service';

import { Item, Stat, UserMetadata } from './minio.model';

@Injectable({ providedIn: 'root' })
export class MinioService {
  private baseGetUrl = '/oss/v1/getservice';
  private baseListUrl = '/oss/v1/listservice';
  private basePutUrl = '/oss/v1/putservice';
  private baseRemoveUrl = '/oss/v1/removeservice';
  private baseStatUrl = '/oss/v1/statservice';
  private bucketName = 'megrez';

  private upload$ = new Subject<UserMetadata>();
  private remove$ = new Subject<UserMetadata>();

  constructor(private http: BaseHttpService) {}

  public get onUpload(): Observable<UserMetadata> {
    return this.upload$.asObservable();
  }

  public get onRemove(): Observable<UserMetadata> {
    return this.remove$.asObservable();
  }

  upload(formData: FormData): Observable<UserMetadata> {
    return this.http.post<UserMetadata>(`${this.basePutUrl}/object/${this.bucketName}`, formData).pipe(
      map(e => new UserMetadata(e)),
      tap(e => {
        this.upload$.next(e);
      })
    );
  }

  download(objectId: string): Observable<Blob> {
    return this.http.get(`${this.baseGetUrl}/object/${this.bucketName}/${objectId}`, null, {
      responseType: 'blob'
    });
  }

  downloads(objectIds: string[]): Observable<Blob> {
    const queryParams = { objectIds: objectIds };
    return this.http.get(`${this.baseGetUrl}/objects/${this.bucketName}`, queryParams, { responseType: 'blob' });
  }

  getPresignedObjectUrl(objectId: string, method: string): Observable<string> {
    return this.http.get(`${this.baseGetUrl}/presigned-url/${this.bucketName}/${objectId}/${method}`, null, {
      responseType: 'text'
    });
  }

  delete(objectId: string): Observable<UserMetadata> {
    const url = `${this.baseRemoveUrl}/object/${this.bucketName}/${objectId}`;
    return this.http.delete<UserMetadata>(url).pipe(
      map(e => new UserMetadata(e)),
      tap(e => {
        this.remove$.next(new UserMetadata(e));
      })
    );
  }

  list(prefix: string, includeUserMetadata?: boolean): Observable<Item[]> {
    const queryParams = { prefix: prefix, includeUserMetadata: includeUserMetadata };
    return this.http
      .get<Item[]>(`${this.baseListUrl}/objects/${this.bucketName}`, queryParams)
      .pipe(map(data => data.map(e => new Item(e))));
  }

  getStat(objectId: string): Observable<Stat> {
    return this.http.get<Stat>(`${this.baseStatUrl}/state/${this.bucketName}/${objectId}`).pipe(map(e => new Stat(e)));
  }

  getStats(objectIds: string[]): Observable<Stat[]> {
    const queryParams = { objectIds: objectIds };
    return this.http
      .get<Stat[]>(`${this.baseStatUrl}/state/${this.bucketName}`, queryParams)
      .pipe(map(data => data.map(e => new Stat(e))));
  }

  getUserMetadata(objectId: string): Observable<UserMetadata> {
    return this.http
      .get<UserMetadata>(`${this.baseStatUrl}/usermetadata/${this.bucketName}/${objectId}`)
      .pipe(map(e => new UserMetadata(e)));
  }

  getUserMetadatas(objectIds: string[]): Observable<UserMetadata[]> {
    const queryParams = { objectIds: objectIds };
    return this.http
      .get<UserMetadata[]>(`${this.baseStatUrl}/usermetadata/${this.bucketName}`, queryParams)
      .pipe(map(data => data.map(e => new UserMetadata(e))));
  }
}
